home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 March / Macworld (1998-03) (Disk 1).dmg / Shareware World / Utilities / Text Processing / Alpha / Tcl / Modes / fortranMode.tcl < prev    next >
Encoding:
Text File  |  1997-10-08  |  20.2 KB  |  665 lines  |  [TEXT/ALFA]

  1. #=============================================================================
  2. # Fortran mode definition and support procs
  3. #
  4. # Features:
  5. # 1.  Keyword colorization (slightly customizable)
  6. # 2.  Fortran-sensitive shift right/left preserve columns 1-6
  7. # 3.  Auto-indentation
  8. # 4.  Line-breaking with Ctl-Opt-J (a la emacs)
  9. # 5.  Subroutine indexing
  10. # 6.  Cmd-double-click subroutine and include-file lookup
  11. # 7.  Customizable comment and continuation characters
  12. #
  13. #------------------------------------------------------------------------------
  14. # Author: Tom Pollard <pollard@chem.columbia.edu>
  15. #
  16. # To Do:  work around grep failure for Unix-format tag files
  17. #
  18. #  8/97 - Updated for new system code.
  19. #  4/97 - Coloring bug fixed.
  20. #  1/96 - Fort::MarkFile no longer marks F90 "end subroutine ..." statements
  21. #         more F90 keywords (will they never cease?)
  22. #  1/96 - user-selectable comment and continuation characters
  23. #         complete F90 keyword set (Thomas Bewley <bewley@rayleigh.stanford.edu>) 
  24. #         F90 functions and comparison operators optionally colorized ( " " )
  25. #         more complete set of C preprocessor commands colorized
  26. #         fixed case-sensitivity problem in line-indent routines
  27. #  1/96 - minor Fort::DblClick bug fix
  28. # 12/95 - more complete keyword set for F90 and HPF (from Tom Scavo)
  29. # 12/95 - cpp keyword colorization (George Nurser <g.nurser@soc.soton.ac.uk>)
  30. #         cmd-dbl-click supports cpp #include now
  31. # 11/95 - added FortBreakLine
  32. #         fixed case-sensitivity bug
  33. # 10/95 - fixed Cmd-Dbl-Click handler to deal w/ new(?) tag file format and
  34. #            improve performance (fortFindSub)
  35. #  9/95 - fixed getFortPrev bug with numbered lines
  36. #       - shiftLeft/Right revert to normal behavior on ill-formatted lines
  37. #  8/95 - auto-indentation is finally speedy and robust
  38. #  5/95 - added Cmd-Dbl-Click handler
  39. #       - added auto-indentation
  40. # 12/94 - fixed funcExpr, Fort::MarkFile search expressions
  41. #       - changed comment character from 'C' to 'c' (should be case-insensitive!)
  42. #       - added 'include' keyword
  43. #       - added FortShiftRight and FortShiftLeft procs
  44. #------------------------------------------------------------------------------
  45.  
  46.  
  47. #================================================================================
  48. alpha::mode Fort 1.0 dummyFort \
  49.   {*.f *.inc *.INC *.fcm *.for *.FOR *.f9 *.f90 *.hpf } {} {
  50.     set unixMode(fortran) {Fort}
  51. }
  52.  
  53. proc dummyFort {} {}
  54.  
  55. newPref f sortedIsDefault    {0} Fort
  56. newPref f wordWrap        {0} Fort
  57. newPref v funcExpr    {^[^cC*!][ \t]*(subroutine|[ \ta-z*0-9]*function|entry).*$} Fort
  58. newPref f autoMark        {0} Fort
  59. newPref f electricTab    {1} Fort
  60.  
  61. # newPref v prefixString {c} Fort
  62. newPref    v continueChar    {$} Fort
  63. newPref    v commentChar    {c} Fort shadowFort
  64. newPref    f colorFuncs    {0} Fort shadowFort
  65. newPref    f colorOpers    {0} Fort shadowFort
  66.  
  67. newPref f indentComment    {0} Fort
  68. newPref v markTag        {{}} Fort
  69.  
  70. #=============================================================================
  71. # Colorize Fortran keywords
  72. #
  73. proc fortColorKeywords {{color blue} {comment red} {specialChars black}} {
  74.     global FortmodeVars
  75.  
  76.     set FortKeywords { 
  77.         allocatable allocate assign backspace block call character close common 
  78.         complex contains continue cycle data deallocate dimension do double else 
  79.         elseif end enddo endfile endif entry equivalence exit external extrinsic 
  80.         forall format function goto if implicit include inquire integer intent 
  81.         interface intrinsic logical module namelist nullify open optional 
  82.         parameter pause pointer precision print private program public pure read 
  83.         real recursive return rewind save sequence stop subroutine target then 
  84.         use where while write assignment case default elsewhere endfile go none 
  85.         operator procedure select to type
  86.     }
  87.     
  88.     if {$specialChars != "black"} {
  89.         regModeKeywords -e $FortmodeVars(commentChar) -c $comment -k $color Fort $FortKeywords  -i {=}  -i {*}  -i {/}  -i {+}  -i {-}  -i {,}  -i {(} -i {)} -I $specialChars
  90.     } else {
  91.         regModeKeywords -e $FortmodeVars(commentChar) -c $comment -k $color Fort $FortKeywords  
  92.     }
  93.     unset FortKeywords
  94.  
  95. #=============================================================================
  96. # Colorize selected C preprocessor keywords
  97. #
  98. proc fortColorCPP {{color green}} {
  99.     set CPPKeywords  {
  100.         #if #endif #include #else #define #undef #ifdef #ifndef
  101.     }
  102.     regModeKeywords -a  -k $color Fort $CPPKeywords
  103.     unset CPPKeywords
  104. }
  105.  
  106.  
  107. #=========================================================================
  108. # Colorize Fortran operators
  109. #
  110. proc fortColorOpers {{color green}} {
  111.     set FortOperators {
  112.         eq ne lt le gt ge not and or eqv neqv true false
  113.     }
  114.     regModeKeywords -a -k $color Fort $FortOperators
  115.     unset FortOperators
  116. }
  117.  
  118. #=========================================================================
  119. # Colorize Fortran function keywords
  120. #
  121. proc fortColorFuncs {{color green}} {
  122.     # Fortran bit functions
  123.     #
  124.     set BitKeywords {
  125.         bit_size btest iand ibclr ibits ibset ieor ior ishft ishftc mvbits not
  126.     }
  127.     regModeKeywords -a -k $color Fort $BitKeywords
  128.     unset BitKeywords
  129.     
  130.     # Fortran intrinsic functions
  131.     #
  132.     set IntrinsicKeywords {
  133.         abs acos aimag asin atan atan2 conjg cos cosh dble dim dprod exp ichar 
  134.         len lge lgt lle llt log log10 max min mod sign sin sinh sqrt tan tanh 
  135.         iabs dabs cabs dacos dint dnint dasin datan datan2 dcos ccos dcosh idim 
  136.         ddim dexp cexp ifix idint alog ddlog clog alog10 dlog10 max0 amax0 max1 
  137.         amax1 dmax1 min0 amin0 min1 amin1 dmin1 amod dmod idnint float sngl 
  138.         isign dsign dsin csin dsinh dsqrt csqrt dtan dtanh aint anint char cmplx 
  139.         index int nint achar adjustl adjustr all allocated any associated 
  140.         bit_size btest ceiling count cshift date_and_time digits dot_product 
  141.         eoshift epsilon exponent floor fraction huge iachar iand ibclr ibits 
  142.         ibset ieor ior ishft ishftc kind lbound len_trim logical matmul 
  143.         maxexponent maxloc maxval merge minexponent minloc minval modulo mvbits 
  144.         nearest not pack precision present product radix random_number 
  145.         random_seed range repeat reshape rrspacing scale scan selected_int_kind 
  146.         selected_real_kind set_exponent shape size spacing spread sum 
  147.         system_clock tiny transfer transpose trim ubound unpack verify
  148.     }
  149.     regModeKeywords -a -k $color Fort $IntrinsicKeywords
  150.     unset IntrinsicKeywords    
  151. }
  152.  
  153. fortColorKeywords blue red magenta
  154. fortColorCPP green
  155. if {$FortmodeVars(colorFuncs)} {
  156.     fortColorFuncs green
  157. }
  158. if {$FortmodeVars(colorOpers)} {
  159.     fortColorOpers green
  160. }
  161. #=============================================================================
  162. # Special Fortran keybindings
  163. #
  164. bind '\[' <c>  FortShiftLeft Fort
  165. bind '\[' <co> FortShiftLeftSpace Fort
  166. bind '\]' <c>  FortShiftRight Fort
  167. bind '\]' <co> FortShiftRightSpace Fort
  168.  
  169. bind 'j'  <zo> FortBreakLine Fort
  170.  
  171. #=============================================================================
  172. # Update colorization when Fortran mode variables are changed
  173. #
  174. proc shadowFort {name2} {
  175.     global HOME FortmodeVars
  176.     switch $name2 {
  177.         "colorFuncs"    {
  178.             if {$FortmodeVars(colorFuncs)} {
  179.                 fortColorFuncs green
  180.             } else {
  181.                 fortColorFuncs black
  182.             }
  183.          }
  184.         "colorOpers"    {
  185.             if {$FortmodeVars(colorOpers)} {
  186.                 fortColorOpers green
  187.             } else {
  188.                 fortColorOpers black
  189.             }
  190.          }
  191.         "commentChar" {    
  192.             fortColorKeywords blue red magenta
  193.         }
  194.         default {
  195.             return
  196.         }
  197.     }
  198. }
  199.  
  200. #=============================================================================
  201. #
  202. proc Fort::MarkFile {} {
  203.     global FortmodeVars
  204.     set tag [quote::Regfind $FortmodeVars(markTag)]
  205.     
  206.     set pat0 {^.*(subroutine|.*function|entry|program).*$}
  207.     set pat1 {^[^cC*!]([ \ta-z*0-9]*)(subroutine|.*function|entry|program)[ \t]+([a-z0-9_]+)}
  208.     set end [maxPos]
  209.     set pos 0
  210.     while {![catch {search -s -f 1 -r 1 -m 0 -i 1 $pat1 $pos} mtch]} {
  211.         regexp -nocase $pat1 [eval getText $mtch] allofit valtyp subtyp name
  212.         set start [lineStart [lindex $mtch 0]]
  213.         set next [nextLineStart $start]
  214.         set pos $next
  215.         if {! [regexp -nocase "end" $valtyp mtch]} {
  216.             set inds([lineStart $start]) $name
  217.         }
  218.         
  219.     }
  220.     
  221.     set pat2 "^(c+${tag})\[ \t\]*(\[^\n\r\]*\[^ \t\])\[^ \t\]*\$"
  222.     set pos 0
  223.     while {![catch {search -s -f 1 -r 1 -m 0 -i 1 $pat2 $pos} mtch]} {
  224.         regexp -nocase $pat2 [eval getText $mtch] allofit cc comment
  225.         regsub -all {[\/\(\)]} $comment {} comment
  226.         set start [lindex $mtch 0]
  227.         set end [nextLineStart $start]
  228.         set pos $end
  229.         set inds([lineStart $start]) $comment
  230.     }
  231.     
  232.     if {[info exists inds]} {
  233.         foreach f [lsort -integer [array names inds]] {
  234.             set next [nextLineStart $f ]
  235.             setNamedMark $inds($f) $f $f $f
  236.         }
  237.     }
  238. }
  239.  
  240. #================================================================================
  241. # Block shift left and right for Fortran mode (preserves cols 1-6)
  242. #================================================================================
  243.  
  244. proc FortShiftLeft {} {
  245.     global shiftChar
  246.     doFortShiftLeft "\t"
  247.     
  248. }
  249. proc FortShiftLeftSpace {} {
  250.     global shiftChar
  251.     doFortShiftLeft " "
  252. }
  253.  
  254. proc doFortShiftLeft {shiftChar} {
  255.     set start [lineStart [getPos]]
  256.     set end [nextLineStart [expr [selEnd] - 1]]
  257.     if {$start >= $end} {set end [nextLineStart $start]}
  258.     
  259.     set text [split [getText $start [expr $end - 1]] "\r"]
  260.     
  261.     set textout ""
  262.     
  263.     set pat {^([cC]|[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9].| *[0-9]*\t)(.*)$}
  264.     foreach line $text {
  265.         if {[regexp $pat $line mtch pref body]} {
  266.             if {[string index $body 0] == $shiftChar} {
  267.                 lappend textout $pref[string range $body 1 end]
  268.             } else {
  269.                 lappend textout $line
  270.             }
  271.  
  272.         } elseif {[string index $line 0] == $shiftChar} {
  273.             lappend textout [string range $line 1 end]
  274.  
  275.         } else {
  276.             lappend textout $line
  277.         }
  278.     }
  279.  
  280.     set text [join $textout "\r"]    
  281.     replaceText $start [expr $end - 1] $text
  282.     select $start [expr 1 + $start + [string length $text]]
  283. }
  284.  
  285. proc FortShiftRight {} {
  286.     global shiftChar
  287.     doFortShiftRight "\t"
  288.     
  289. }
  290. proc FortShiftRightSpace {} {
  291.     global shiftChar
  292.     doFortShiftRight " "
  293. }
  294.  
  295. proc doFortShiftRight {shiftChar} {
  296.     set start [lineStart [getPos]]
  297.     set end [nextLineStart [expr [selEnd] - 1]]
  298.     if {$start >= $end} {set end [nextLineStart $start]}
  299.     
  300.     set text [split [getText $start [expr $end - 1]] "\r"]
  301.     
  302.     set textout ""
  303.     
  304.     set pat {^([cC]|[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9].| *[0-9]*\t)(.*)$}
  305.     foreach line $text {
  306.         if {[regexp $pat $line mtch pref body]} {
  307.             lappend textout $pref$shiftChar$body
  308.         } else {
  309.             lappend textout $shiftChar$line
  310.         }
  311.     }
  312.     
  313.     set text [join $textout "\r"]    
  314.     replaceText $start [expr $end - 1] $text
  315.     select $start [expr 1 + $start + [string length $text]]
  316. }
  317.  
  318. proc FortBreakLine {} {
  319.     global FortmodeVars
  320.     set pos [getPos]
  321.     set line [getText [lineStart $pos] [expr [nextLineStart $pos]-1]]
  322.     if {[regexp {^[cC*!]} $line char]} {
  323.         insertText "\n$char "
  324.     } else {
  325.         set char $FortmodeVars(continueChar)
  326.         insertText "\n     $char"
  327.     }
  328.     FortindentLine
  329. }
  330.  
  331. #=============================================================================
  332. # Cmd-double-clicking opens include files, jumps to subroutine definitions,
  333. # and follows tags.
  334. #
  335. proc Fort::DblClick {from to} {
  336.     global tagFile
  337.     set pat1 {^[^cC*!][ \ta-z*0-9]*(subroutine|.*function|entry)[ \t]+}
  338.     set incPat {^[^cC*!][ \t]*include[ \t]*['"]([^'"]+)['"]}
  339.  
  340.     # First check whether an 'include' was clicked
  341.     set line [getText [lineStart $from] [expr [nextLineStart $to] - 1]]
  342.     if {[regexp -nocase $incPat $line allofit fname]} {
  343.         set path [absolutePath $fname]
  344.         if {[catch {openFileQuietly $path}]} { 
  345.             message "include file \'$fname\' not found in source folder"
  346.         }
  347.         return
  348.     }
  349.     
  350.     select $from $to
  351.     set text [getSelect]
  352.     
  353.     # First check current file for subroutine definition,...
  354.     if {![catch {fortFindSub $text} mtch]} { 
  355.         regexp -nocase $pat1 [eval getText $mtch] allofit subtyp name
  356.         pushPosition
  357.         display [lindex $mtch 0]
  358. #         eval select $mtch
  359.         message "press <Ctl .> to return to original cursor position"
  360.  
  361.     # ...then check tags file.
  362.     } else {
  363.         message "Searching tags file..."
  364.         set lines [grep "^$text'" $tagFile]
  365.         if {[regexp {'(.*)'} $lines dummy fname]} { 
  366.             pushPosition
  367.             if {[string match "*$fname*" [winNames -f]]} {
  368.                 bringToFront $fname
  369.             } else {
  370.                 edit $fname
  371.             }
  372.             set inds [fortFindSub $text]
  373. #             set inds [search -s -f 1 -r 1 -i 1 "$pat1$text" 0]
  374.             display [lindex $inds 0]
  375. #             eval select $inds
  376.             message "press <Ctl .> to return to original cursor position"
  377.         }
  378.     }
  379. }
  380.  
  381. # Speedy search for a Fortran subroutine.  Performance is dramatically 
  382. # improved by scanning for the name alone first, rather than running 
  383. # complicated regexp search on the entire file.
  384. #
  385. proc fortFindSub {name} {
  386.     set pat1 {^[^cC*!][ \ta-z*0-9]*(subroutine|.*function|entry)[ \t]+}
  387.     set pos 0
  388.     while {![catch {search -s -f 1 -r 0 -m 0 -i 1 $name $pos} mtch]} {
  389.         set beg [lineStart [lindex $mtch 0]]
  390.         set end [expr [nextLineStart [lindex $mtch 1]] -1]
  391.         set line [getText $beg $end]
  392.         if {[regexp  -nocase $pat1$name $line allofit subtyp name]} {
  393.             return $mtch 
  394.         } else {
  395.             set pos [lindex $mtch 1]
  396.         }
  397.     }
  398.     error "Subroutine \"$name\" not found"
  399. }
  400.  
  401. #=============================================================================
  402. # Fortan auto-indentation
  403. #
  404. # Logic:
  405. #    0.    Identify previous line
  406. #            a) ignore comments and continuation lines
  407. #            b) if current line is a CONTINUE that matches a DO, use the
  408. #                first corresponding DO as the previous line
  409. #
  410. #    1.    Find leading whitespace for previous line
  411. #
  412. #    2.    Increase whitespace if previous line starts a block, i.e.,
  413. #            a) DO loop
  414. #            b) IF ... THEN 
  415. #            c) ELSE
  416. #
  417. #    3.    Decrease whitespace if current line ends a block, i.e.,
  418. #            a) ELSE || ENDIF || END IF || ENDDO || END DO
  419. #            b) <linenum> CONTINUE matching a preceding DO
  420. #
  421. #        or if previous line ends a DO loop on an executable statement, i.e.,
  422. #            c) <linenum> (not CONTINUE) matching a preceding DO
  423. #
  424. ####################################################################################
  425. # Fortan auto-indentation
  426. #
  427. proc FortindentLine {} {    
  428.     set bol [lineStart [getPos]]
  429.     set eol [expr [nextLineStart $bol] - 1]
  430.     Fortindent $bol $eol
  431. }
  432.  
  433. proc FortindentRegion {} {    
  434.     Fortindent [getPos] [selEnd]
  435. }
  436.  
  437. ####################################################################################
  438. # Fortan auto-indentation of a specified region
  439. #
  440. proc Fortindent {pos0 pos1} {
  441.     global fortDooz fortPrevLine fortTop msg
  442.     global FortmodeVars
  443.  
  444.     set tag [quote::Regfind $FortmodeVars(markTag)]
  445.     set doComment $FortmodeVars(indentComment)
  446.  
  447.     # Define regexps
  448.     set subPat {^[^cC*!][ \ta-z*0-9]*(subroutine|.*function|entry|program)[ \t]+([a-z0-9_]+)}
  449.     set bolPat {^[^cC*!\n\r][ \t]*[^ \t\n\r][^\r\n]*$}
  450.     set mtPat {^[ \t]*$}
  451.     set tab "    "
  452.     
  453.     set contPat {^     ([^ \t\n\r])[^\r\n]*$}
  454.     set lnumPat {^([ \t]*)([0-9]*)([ \t]*)(.*)$}
  455.     set comPat "^(\[cC*!\]+(${tag})?)(\[ \t\]*)(.*)\$"
  456.     set doPat {^[^cC*!\n\r][ \t]*do[ \t]+}
  457.     set tailPat {[^\r\n]*$}
  458.     
  459.     set bobPat {^(if[^\n\r]*then|else|do)}
  460.     set eobPat {^(end[ \t]*if|end[ \t]*do|else)}
  461.     set enddoPat {^(end[ \t]*do|continue)}
  462.     
  463. #     set fortTop [fortSubTop $pos0]
  464.     set fortTop -1
  465.     
  466.     catch {unset fortDooz}
  467.     set fortPrevLine ""
  468.     
  469.     # Loop over region line by line
  470.     set from [lindex [posToRowCol $pos0] 0]
  471.     set to [lindex [posToRowCol $pos1] 0]
  472.     
  473.     while {$from <= $to} {        
  474.         set msg "Indenting line $from"
  475.         message $msg
  476.         set bol [lineStart [rowColToPos $from 0]]
  477.         set eol [expr [nextLineStart $bol] - 1]
  478.         set thisLine [getText $bol $eol]
  479.         goto $bol
  480.         
  481.         # Check whether we're entering a new routine
  482.         #
  483.         if {[regexp $subPat $thisLine allofit subType subName]} {
  484.             # alertnote "entering subr: \/$subName\/"
  485.             set fortTop $bol
  486.             catch {unset fortDooz}
  487.         } 
  488.         
  489.         # Is the current line a comment line...
  490.         #        
  491.         if {[regexp $comPat $thisLine allofit cc tag pre body]} {
  492.             if {$FortmodeVars(indentComment) > 0} {
  493.                 set body [string trimright $body]
  494.                 # alertnote "comment line: \/$pre\/$body\/"
  495.                 set lwhite "$cc     "
  496.                 
  497.                 replaceText $bol $eol $lwhite$body
  498.             }
  499.             
  500.         # ... or a line of code (possibly empty)?
  501.         #    
  502.         } elseif {[regexp $lnumPat $thisLine allofit pre lnum post body]} {
  503.             set body [string trimright $body]
  504.             # alertnote "line: \/$pre\/$lnum\/$post\/$body\/"
  505.             
  506.             # is it a continuation line?
  507.             #
  508.             if {(![regexp {\t} $pre]) && ([string length $pre] == 5)} {
  509.                 set cont [string index $lnum$post$body 0]
  510.                 set body [string trimleft [string range $lnum$post$body 1 end]]
  511.             } else {
  512.                 set cont {}
  513.             }
  514.             # alertnote "cont: \/$cont\/"
  515.             
  516.             # get whitespace for preceding line
  517.             set enddo [getFortPrev $bol $lnum]
  518.             set lwhite [getFortLwhite $bol]
  519.             
  520.             # if this line ends a block, decrease the whitespace
  521.             if {[regexp $eobPat $body] || ($enddo && [regexp -nocase $enddoPat $body])} {
  522.                 set lwlen [expr [string length $lwhite] - 4]
  523.                 set lwhite [string range $lwhite 0 $lwlen]
  524.             } 
  525.             
  526.             if {[string length $lnum]} {
  527.                 if {[string index $lwhite 0] != $tab} {
  528.                     set lwhite [string range $lwhite [expr [string length $lnum] +1] end]
  529.                 }
  530.                 set lnum " $lnum"
  531.             }
  532.             # alertnote "lwhite: \/$lwhite\/ len: [string length $lwhite]"
  533.             # message "$msg : replacing text      "
  534.             
  535.             if {[string length $cont]} {
  536.                 replaceText $bol $eol "     $cont$lwhite$body"    
  537.             } else {
  538.                 replaceText $bol $eol $lnum$lwhite$body
  539.                 if {[string length $body] > 0} {
  540.                     set fortPrevLine $lnum$lwhite$body
  541.                 }
  542.             }
  543.         } else {
  544.             # message "$msg : Couldn't parse line         "
  545.         }
  546.         
  547.         # message "$msg : Done                "
  548.         incr from
  549.     }
  550. }
  551.  
  552. proc getFortLwhite {bol} {
  553.     global fortDooz fortPrevLine fortTop msg
  554.     # Define regexps
  555.     set tab "    "
  556.     set lnumPat {^([ \t]*)([0-9]*)([ \t]*)(.*)$}
  557.     set doPat {^[^cC*!\n\r][ \t]*do[ \t]+}
  558.     set bobPat {^(if[^\n\r]*then|else|do)}
  559.     set enddoPat {^(end[ \t]*do|continue)}
  560.     
  561.     if {[regexp $lnumPat $fortPrevLine allofit pre0 lnum0 post0 body0]} {
  562.         # alertnote "prevLine: \/$pre0\/$lnum0\/$post0\/$body0\/"
  563.         
  564.         if {[string length $lnum0]} {
  565.             if {[string index $post0 0] == $tab} {
  566.                 set lwhite $post0
  567.             } else {
  568.                 regsub -all {[0-9]} $pre0$lnum0$post0 { } lwhite
  569.             }
  570.         } else {
  571.             set lwhite $pre0
  572.         }
  573.         # alertnote "lwhite: \/$lwhite\/ len: [string length $lwhite]"
  574.         # message "$msg : got lwhite (initial)"
  575.         
  576.         # if there's a line number and it's not a CONTINUE or ENDDO, 
  577.         # then check for a matching DO statement and adjust 
  578.         # indentation if found
  579.         #
  580.         if {[string length $lnum0] && ![regexp -nocase $enddoPat $body0]} {
  581.             if {[getFortPrev [lineStart [expr $bol - 1]] $lnum0]} {
  582.                 set lwlen [expr [string length $lwhite] - 4]
  583.                 set lwhite [string range $lwhite 0 $lwlen]
  584.  
  585.             }
  586.         }
  587.         
  588.         # If the preceeding line begins a block (IF-THEN, DO, or ELSE),
  589.         # then increase the whitespace
  590.         #    
  591.         if {[regexp -nocase $bobPat $body0]} {
  592.             set lwhite "$lwhite   "
  593.             
  594.             if {[regexp -nocase "$doPat\(\[0-9\]+\)" $body0 mtch donum]} {
  595.                 set eol [expr [nextLineStart $bol] - 1]
  596.                 set fortDooz($donum) [getText $bol $eol]
  597.             }
  598.         }
  599.         # message "$msg : got lwhite (final)  "
  600.     }
  601.     return "$lwhite"
  602. }
  603.  
  604. proc getFortPrev {bol lnum} {        
  605.     global fortDooz fortPrevLine fortTop msg
  606.     # Define regexps
  607.     set doPat {^[^cC*!\n\r][ \t]*do[ \t]+}
  608.     set bolPat {^[^cC*!\n\r][ \t]*[^ \t\n\r][^\r\n]*$}
  609.     set contPat {^     ([^ \t\n\r])[^\r\n]*$}
  610.  
  611.     # if there's a line number, check for a matching DO statement ...
  612.     if {[string length $lnum]} {
  613.         if {[lsearch [array names fortDooz] $lnum] >= 0} {
  614.             set fortPrevLine $fortDooz($lnum)
  615.             return 1
  616.         } else {
  617.             if {$fortTop < 0} {
  618.                 set fortTop [fortSubTop $bol]
  619.             }
  620.             if {![catch {search -s -f 0 -r 1 -i 1 -l $fortTop $doPat$lnum [expr $bol -1]} dolst]} {
  621.                 set fortPrevLine [eval getText $dolst]
  622.                 set fortDooz($lnum) $fortPrevLine
  623.                 # alertnote "doLine0: \/$fortPrevLine\/"
  624.                 return 1
  625.             }
  626.         }
  627.     }
  628.         
  629.     # ... otherwise find the first preceding non-comment, non-continuation line
  630.     if {[string length $fortPrevLine] == 0} {
  631.         if {[catch {
  632.             set lst [search -s -f 0 -r 1 -i 1 -s $bolPat [expr $bol-1]]
  633.             set fortPrevLine [eval getText $lst]
  634.             while {[regexp -nocase $contPat $fortPrevLine]} {
  635.                 set lst [search -s -f 0 -r 1 -i 1 $bolPat [expr [lindex $lst 0] - 1]]
  636.                 set fortPrevLine [eval getText $lst]
  637.             }
  638.         }]} {
  639.             # if search fails, we're at the top of a file, so reset indentation
  640.             set fortPrevLine "      continue"
  641.         }
  642.     }
  643.     
  644.     # alertnote "prevLine: \/$fortPrevLine\/"
  645.     # message "$msg : got prevLine"
  646.     return 0
  647. }
  648.  
  649. # Find the beginning of the current subroutine
  650. #
  651. proc fortSubTop {{pos 0}} {
  652.     if {$pos == 0} {
  653.         set pos [lineStart [getPos]]
  654.     }
  655.     set subPat {^[^cC*!][ \ta-z*0-9]*(subroutine|.*function|entry|program)[ \t]+([a-z0-9_]+)}
  656.     
  657.     if {![catch {search -s -f 0 -r 1 -m 0 -i 1 $subPat $pos} sublst]} {
  658.         # set subLine [eval getText $sublst]
  659.         # alertnote "subLine: \/$subLine\/"
  660.         return [lindex $sublst 0]
  661.     } else {
  662.         return 0
  663.     } 
  664. }